#include "file_opera.h"

fifo_s_t file_name_fifo;

FRESULT fatTest_ReadTXTFile(TCHAR *filename, uint8_t* str, uint16_t size)
{
	FIL file;
	UINT bytesRead;
	uint32_t filesize = 0;
	uint32_t start_pos = 0;
	FRESULT res = f_open(&file, filename, FA_READ);
	if (res == FR_OK)
	{
		filesize = f_size(&file);
		start_pos = (filesize > size) ? (filesize - size) : 0;
		res = f_lseek(&file, start_pos);
		if(res == FR_OK)
		{
			res = f_read(&file, str, size, &bytesRead);
			str[bytesRead] = '\0';
		}
	}
	f_close(&file);
	return res;
}

/**
  * @brief  根据字符串判断文件类型，支持.wav .txt文件
  * @param  str 输入字符串
  * @param  filename 输出字符串，目前仅用于判断.wav文件的文件名
  *         格式：+IPD,0,xx(后续信息的长度):File name:xxxxxx.wav\r\nFile size:xxxxxxxxxx(byte)\0
  *         @param  message 在filename后跟着的信息
  * @retval filetypes
  */
filetypes get_MessType(uint8_t* str, uint8_t* filename, uint8_t* filesize)
{
	char *str_start = strstr((char *)str, "File name:");
	if(str_start != NULL)
	{
		str_start += 10;							//将指针指向File name:后一位的地址处
		char *str_end = str_start;
		while (*str_end != '\0' && *str_end != '\r' && *str_end != '\n')
		{	str_end++;   }
		size_t len = str_end - str_start;
		strncpy((char *)filename, str_start, len);
		filename[len] = '\0';

		str_start = strstr((char *)str, "File size:");
		str_start += 10;							//将指针指向File size:后一位的地址处
		str_end = str_start;
		while (*str_end != '\0' && *str_end != '\r' && *str_end != '\n')
		{	str_end++;   }
		len = str_end - str_start;
		strncpy((char *)filesize, str_start, len);
		filesize[len] = '\0';
	}
	if(strstr((char *)str, "+IPD") != NULL)			//ATK8266 通讯数据头
	{
		if(strstr((char *)str, "CMD") != NULL)		//自定义指令头
		{	return file_txt;	}
		else
		{	return file_wav;	}
	}
	else
	{	return file_txt;	}
}

DWORD fat_GetFatTimeFromRTC()
{
	return 0;
}

extern osSemaphoreId_t BinSem_usart3_getHandle;
extern osThreadId_t Task_Wav_CreateHandle;
extern osThreadId_t Task_LCD_ShowWaHandle;
extern osThreadId_t Task_ESP8266Handle;
extern osMessageQueueId_t Queue_usart3_rawHandle;
extern osMessageQueueId_t Queue_FileTypeHandle;
extern osMessageQueueId_t Queue_FileTypeWritingHandle;
extern osMessageQueueId_t Queue_FileEventHandle;
extern osMessageQueueId_t Queue_FileSizeTotalHandle;
FIL file;
//DIR dir;

// 函数用于计算数组元素的和
static uint16_t array_sum(uint16_t *arr, uint16_t size)
{
    int sum = 0;
    for (int i = 0; i < size; i++)
    {	sum += arr[i];}
    return sum;
}

// 函数用于在数组前端插入字符串
static void insert_string_at_front(uint8_t *target_array, uint16_t target_arraySize, const uint8_t *insert_arrary)
{
	uint16_t insert_len = strlen((char *)insert_arrary);

    for (int16_t i = target_arraySize; i >= 0; i--)		// 从数组末尾开始，将元素向后移动插入字符串的长度
    {	target_array[i + insert_len] = target_array[i]; }

    memmove(target_array, insert_arrary, insert_len);	// 复制插入字符串到数组前端
}

// 函数用于返回数组的有效数据长度，方法为从数组末端开始搜索\0
static uint16_t get_array_validSize(uint8_t *array, uint16_t total_len)
{
	for(int16_t i = total_len-1; i >= 0;i--)
	{
		if(array[i] != 0)
		{return i+1;}
	}
	return 0;
}

#define usart_half_len (usart_receive_len / 2)
static uint8_t temp_buffer[usart_half_len]; // 暂存不足半数的数据
static uint16_t temp_len = 0;          		// 暂存数据的有效长度
/**
  * 该函数用于将usart3_rawdata数组中fifo_len数量的元素压入暂存缓冲区，待暂存缓冲区满时才将数据写入SD卡
  * usart3_rawdata	：传入数据的起始位置
  * fifo_len		：传入的字节数
  */
static void process_usart_data(uint8_t *usart3_rawdata, uint16_t fifo_len)
{
    uint16_t total_available = temp_len + fifo_len;

    if (total_available >= usart_half_len)	// 收集的数据已超过半数
    {
        // 计算需要从新数据中拷贝的字节数
        uint16_t copy_from_new = usart_half_len - temp_len;
        // 创建临时写入缓冲区
        uint8_t write_buffer[usart_half_len];
        // 合并暂存数据和新数据
        memcpy(write_buffer, temp_buffer, temp_len);
        memcpy(write_buffer + temp_len, usart3_rawdata, copy_from_new);
        // 文件写入
        UINT bw = 0;
        f_write(&file, write_buffer, usart_half_len, &bw);
        // 剩余的数据写入temp_buffer缓冲区，以供下次拼接
        uint16_t remaining_new = fifo_len - copy_from_new;
        if (remaining_new > 0)
        {
            memcpy(temp_buffer, usart3_rawdata + copy_from_new, remaining_new);
            temp_len = remaining_new;
        }
        else
        {	temp_len = 0;}
    }
    else 	// 收集的数据尚未超过半数，暂存新数据等待后续拼凑
    {
        memcpy(temp_buffer + temp_len, usart3_rawdata, fifo_len);
        temp_len += fifo_len;
    }
}

#ifdef SD_Write_New
/**
  * 该线程用于将ESP8266发送过来的数据写入SD中，包含以下功能
  * 1、能根据ESP8266数据头+IPD判断有效数据的大小，并自动剔除数据头
  * 2、能通过上位机传输来的文件名称和大小等信息自适应写入log文件或者需传输的文件
  * 3、当检测到上位机传输的为文件时，能通知AppTask_LCD_ShowWavSize线程实时显示传输进度
  * 4、当检测到数据丢包、传输中断、传输完成时，会通知AppTask_ESP8266线程向上位机发送指定信息
  * 存在的问题
  * 1、当传输过程中丢包，传输会中止；文件传输的所需的时间大于10s左右就极易丢包，大小大于2Mbyte
  * 2、文件传输过程中常会会出现该线程调度异常情况，结果为写入数据中断，以下为排查到的问题的
  * 	1）传输过程中，AppTask_LCD_ShowWavSize依然正常运行，并会在3s后报警
  * 	2）中断正常使用，但freeRTOS会卡在portTASK_FUNCTION这里面
  * 	3）查询到AppTask_SD_Write为阻塞状态，但不确定阻塞位置和获取二值信号量是否运行正常
  */
void AppTask_SD_Write(void *argument)
{
	/* USER CODE BEGIN AppTask_SD_Write */
	extern fifo_s_t uart_rx_fifo;
	extern uint8_t *writing_buffer;
	filetypes file_type;
	filetypes file_type_writing;
	fifo_s_init(&uart_rx_fifo, writing_buffer, usart_receive_len / 2);
	uint8_t usart3_rawdata[usart_receive_len/2 + 100] = {0};
	uint8_t *write_pos = usart3_rawdata;
	uint8_t *str_dataSizeStart_pos = usart3_rawdata;
	FRESULT res = f_mount(&SDFatFS, "0:", 1);

	uint8_t istime_check_filetype = SET;
//	uint8_t istime_get_datasize = SET;
	uint8_t filename[30] = {0};
	uint8_t str_filesize[10] = {0};
	int16_t data_remain = 0;
	const char str_handle[] = "+IPD,0,";
//	const char str_path[] = "Music";
	uint16_t valid_len = 0;
	uint8_t front_dataRemain[15] = {0};
//	uint16_t test_dataReamin[100] = {0};
//	uint16_t test_fifo_enter_len[100] = {0};
//	uint16_t test_array_fifo_len[100] = {0};
//	uint16_t test_enter_times = 0;
//	uint8_t test_front_dataRemain[15] = {0};
//	uint8_t test_front_rawdata[usart_receive_len/2 + 100] = {0};
//	uint16_t test_enter1_times = 0;

	if (res == FR_OK)
	{	lcd_show_string(30, 40 + 1 * 30, 240, 24, 24, "FatFS mount ok", BLACK);}
	else
	{
		lcd_show_string(30, 40 + 1 * 30, 240, 24, 24, "No file system!", RED);
		BYTE workBuffer[4 * BLOCKSIZE];
		DWORD cluster_size = 0;
		FRESULT res = f_mkfs("0:", FM_FAT32, cluster_size, workBuffer, 4 * BLOCKSIZE);
		if (res == FR_OK)
		{	lcd_show_string(30, 40 + 2 * 30, 240, 24, 24, "Format OK!", RED);	}
		else
		{	lcd_show_string(30, 40 + 2 * 30, 240, 24, 24, "Format Fail!", RED);	}
	}

	/* Infinite loop */
	for (;;)
	{
		if(xSemaphoreTake(BinSem_usart3_getHandle, portMAX_DELAY) == pdTRUE)
		{
			uint16_t handle_size[10] = {0};
			uint16_t handle_times = 0;
			uint16_t data_size = 0;
			uint16_t fifo_len = 0;
			filetypes fileEvent;

			//开始前确认是否有事件需要处理，包括文件已传输完成事件、文件已传输一部分事件
			BaseType_t queueEvent_res = xQueueReceive(Queue_FileEventHandle, &fileEvent, 0);
			if(queueEvent_res == pdTRUE)
			{
				if(fileEvent == file_wav_full)
				{
					file_type_writing = file_wav_full;
					file_type = file_stop;
					xQueueOverwrite(Queue_FileTypeHandle, &file_type);
					uint32_t file_currentSize = f_size(&file);
					xTaskNotify(Task_ESP8266Handle, file_currentSize, eSetValueWithOverwrite);
					goto SkipWrite;
				}
				else if(fileEvent == file_wav_break)
				{	goto FileBreakEvent;	}
			}

			//从fifo中获取串口接收到的数据
			fifo_len = fifo_s_used(&uart_rx_fifo);
			if((file_type_writing == file_wav) && (fifo_len < 150))
			{	goto SkipWrite;	}
			fifo_s_gets(&uart_rx_fifo, (char *)usart3_rawdata, fifo_len);		//获取USART3发来的原始数据
			if(fifo_len > sizeof(usart3_rawdata))
			{	fifo_len = get_array_validSize(usart3_rawdata, sizeof(usart3_rawdata));		}

			//检查是否需要打开/新建文件
			if(istime_check_filetype == SET)
			{
				istime_check_filetype = RESET;
				file_type = get_MessType(usart3_rawdata, filename, str_filesize);
				if(file_type == file_txt)
				{	f_open(&file, "log.txt", FA_OPEN_APPEND | FA_WRITE);	}
				else if(file_type == file_wav)
				{
//					f_opendir(&dir, str_path);
					f_open(&file, (char *)filename, FA_CREATE_ALWAYS | FA_WRITE);
					xQueueOverwrite(Queue_FileTypeHandle, &file_type);
					fifo_s_puts(&file_name_fifo, (char *)filename, sizeof(filename));
					file_type_writing = file_wav;
					xQueueOverwrite(Queue_FileTypeWritingHandle, &file_type_writing);
					if(str_filesize[0] != '\0')
					{
						uint32_t filesize = atoi((char *)str_filesize);
						xTaskNotify(Task_LCD_ShowWaHandle, filesize, eSetValueWithOverwrite);
						xQueueOverwrite(Queue_FileSizeTotalHandle, &filesize);
					}
					goto SkipWrite;
				}
			}

			//写wav文件操作
			if(file_type_writing == file_wav)
			{
				if(front_dataRemain[0] != '\0')			//检查是否有上次传输遗留的数据
				{
					insert_string_at_front(usart3_rawdata, fifo_len, front_dataRemain);
					fifo_len += get_array_validSize(front_dataRemain, sizeof(front_dataRemain));
					memset(front_dataRemain, 0, sizeof(front_dataRemain));
				}
				if(str_dataSizeStart_pos == usart3_rawdata)
				{
					str_dataSizeStart_pos = (uint8_t *)strstr((char *)usart3_rawdata, str_handle);	//找到"+IPD,0,"
					str_dataSizeStart_pos += sizeof(str_handle) - 1;		//将指针设置到"+IPD,0,"之后
				}
				else
				{
					str_dataSizeStart_pos = (uint8_t *)strstr((char *)str_dataSizeStart_pos, str_handle);	//找到"+IPD,0,"
					str_dataSizeStart_pos += sizeof(str_handle) - 1;		//将指针设置到"+IPD,0,"之后
				}
				setAgagin:
				if(	(str_dataSizeStart_pos[0] > '0') && (str_dataSizeStart_pos[0] <= '9') &&
					(str_dataSizeStart_pos[1] > '0') && (str_dataSizeStart_pos[2] <= '9')	)
				{	data_size = atoi((char *)str_dataSizeStart_pos);	}		//字符串转十进制数值
				else 	//数据错误
				{
					FileBreakEvent:
					valid_len = 0;
					data_remain = 0;
					str_dataSizeStart_pos = usart3_rawdata;
					memset(temp_buffer, 0, sizeof(temp_buffer));
					temp_len = 0;
					uint32_t file_currentSize = f_size(&file);	//通知AppTask_ESP8266向上位机发送错误信息
					xTaskNotify(Task_ESP8266Handle, file_currentSize, eSetValueWithOverwrite);
					file_type_writing = file_wav_break;
					file_type = file_stop;
					xQueueOverwrite(Queue_FileTypeHandle, &file_type);
					goto SkipWrite;
				}
				write_pos = (uint8_t *)strstr((char *)str_dataSizeStart_pos, ":") + 1;	//写指针设置到':'之后
				handle_size[handle_times] = write_pos - str_dataSizeStart_pos + 9;		//从\r\n开始到':'的大小
				memmove(str_dataSizeStart_pos - 9, write_pos, 				//从原始数据的\r\n开始写
						fifo_len - array_sum(handle_size, 3) - valid_len);	//fifo大小-(有效数据地址-原始起始地址)
				data_remain = data_size -									//通过数据头解析出的这一组数据还有多少没被接收
						(fifo_len - array_sum(handle_size, sizeof(handle_size)/sizeof(handle_size[0]))- valid_len);

				if(data_remain < -15)	//判断剩余有效数据的量
				{	//还包含下一组数据的数据头未解析
					str_dataSizeStart_pos = write_pos + data_size - handle_size[handle_times] + 9;
					valid_len += data_size;
					handle_times ++;
					goto setAgagin;
				}
				else if((data_remain >= -15) && (data_remain < 0))
				{	//下一组数据头仅传输了一部分，剩下的需待下一组串口数据才能拼接成完整的头
					valid_len += data_size;
					for(int16_t a = data_remain; data_remain < 0; data_remain++)
					{	front_dataRemain[data_remain - a] = usart3_rawdata[valid_len + data_remain - a];}
					str_dataSizeStart_pos = usart3_rawdata;
					fifo_len = valid_len;
					valid_len = data_remain;
//						memset(test_front_dataRemain, 0, 15);
// 						memmove(test_front_dataRemain, front_dataRemain, 15);
				}
				else if(data_remain >= 0)
				{	//当前串口的一组数据已解码完成，由于WIFI模块的一组还剩data_remain多个字节未被接收,
					str_dataSizeStart_pos = usart3_rawdata + data_remain;	//故下一组数据指针需偏移一定量
					valid_len += data_size - data_remain;
					fifo_len = valid_len;
					valid_len = data_remain;
				}
				//传输文件写操作，需当数据达到usart_receive_len的一半长度才执行写文件操作
				process_usart_data(usart3_rawdata, fifo_len);
//				test_enter_times = 10;
				goto SkipWrite;
			}

			//普通log文件写操作
			UINT bw = 0;
			f_write(&file, usart3_rawdata, fifo_len, &bw);

			SkipWrite:
//			memmove(test_front_rawdata, usart3_rawdata, sizeof(test_front_rawdata));
//			test_dataReamin[test_enter_times] = data_remain;
//			test_array_fifo_len[test_enter_times] = fifo_len;
//			test_enter_times++;
//			if(test_enter_times >= 100){	test_enter_times = 0;	}
			memset(usart3_rawdata, 0, sizeof(usart3_rawdata));
			f_sync(&file);

			//判断是否结束当前文件的读写操作
			BaseType_t queue_res = xQueueReceive(Queue_FileTypeHandle, &file_type, 0);
			if(queue_res == pdTRUE)
			{
				if(file_type == file_stop)
				{	//接收检束，在中断中检测到最终字符为\r\n
					istime_check_filetype = SET;
					f_close(&file);
//					f_closedir(&dir);
					LED1_Toggle();
					memset(temp_buffer, 0, usart_half_len);
					temp_len = 0;
					if(file_type_writing == file_wav)		//更新正在写入的文件状态
					{
						file_type_writing = file_wav_end;
						uint32_t file_currentSize = f_size(&file);
						xTaskNotify(Task_ESP8266Handle, file_currentSize, eSetValueWithOverwrite);
					}
					xQueueOverwrite(Queue_FileTypeWritingHandle, &file_type_writing);
					valid_len = 0;
					data_remain = 0;
					str_dataSizeStart_pos = usart3_rawdata;
				}
			}
		}
		osDelay(1);
	}
	/* USER CODE END AppTask_SD_Write */
}

#else

void AppTask_SD_Write(void *argument)
{
	/* USER CODE BEGIN AppTask_SD_Write */
	extern fifo_s_t uart_rx_fifo;
	extern uint8_t *writing_buffer;
	filetypes file_type;
	fifo_s_init(&uart_rx_fifo, writing_buffer, usart_receive_len);
	uint8_t usart3_rawdata[usart_receive_len] = {0};
	FRESULT res = f_mount(&SDFatFS, "0:", 1);

	if (res == FR_OK)
	{
		lcd_show_string(30, 40 + 1 * 30, 240, 24, 24, "FatFS mount ok", BLACK);
	}
	else
	{
		lcd_show_string(30, 40 + 1 * 30, 240, 24, 24, "No file system!", RED);
		BYTE workBuffer[4 * BLOCKSIZE];
		DWORD cluster_size = 0;
		FRESULT res = f_mkfs("0:", FM_FAT32, cluster_size, workBuffer, 4 * BLOCKSIZE);
		if (res == FR_OK)
		{	lcd_show_string(30, 40 + 2 * 30, 240, 24, 24, "Format OK!", RED);	}
		else
		{	lcd_show_string(30, 40 + 2 * 30, 240, 24, 24, "Format Fail!", RED);	}
	}

	/* Infinite loop */
	for (;;)
	{
		if(xSemaphoreTake(BinSem_usart3_getHandle, portMAX_DELAY) == pdTRUE)
		{
			static uint8_t istime_check_filetype = SET;
			static uint8_t filename[30] = {0};
			static uint8_t test_array_remain[usart_receive_len/2] = {0};
			static uint16_t test_array_fifo_len[200] = {0};
			static uint16_t test_enter_times = 0;
			uint16_t fifo_len = 0;
			fifo_len = fifo_s_used(&uart_rx_fifo);
			fifo_s_gets(&uart_rx_fifo, (char *)usart3_rawdata, fifo_len);		//获取USART3发来的原始数据

			if(istime_check_filetype == SET)		//检查是否需要打开/新建文件
			{
				istime_check_filetype = RESET;
				file_type = get_MessType(usart3_rawdata, filename);
				if(file_type == file_txt)
				{
					f_open(&file, "log.txt", FA_OPEN_APPEND | FA_WRITE);
				}
				else if(file_type == file_wav)
				{
					f_open(&file, (char *)filename, FA_OPEN_ALWAYS | FA_WRITE);
					xQueueOverwrite(Queue_FileTypeHandle, &file_type);
					goto SkipWrite;
				}
			}

//			if(fifo_len < 4096)
//			{
//				memmove(test_array_remain, usart3_rawdata, fifo_len);
//			}
//			if(test_array_remain[0] != 0)
//			{
//				insert_string_at_front(usart3_rawdata, fifo_len, test_array_remain, test_array_fifo_len[test_enter_times - 1]);
//				memset(test_array_remain, 0, sizeof(test_array_remain));
// 			}
			test_array_fifo_len[test_enter_times] = fifo_len;
			test_enter_times++;

			process_usart_data(usart3_rawdata, fifo_len);
//			UINT bw = 0;
//			f_write(&file, usart3_rawdata, fifo_len, &bw);
			SkipWrite:
			memset(usart3_rawdata, 0, fifo_len);
			f_sync(&file);
			BaseType_t queue_res = xQueueReceive(Queue_FileTypeHandle, &file_type, 0);
			if(queue_res == pdTRUE)
			{
				if(file_type == file_stop)
				{
					istime_check_filetype = SET;
					f_close(&file);
					LED1_Toggle();
					xTaskNotify(Task_Wav_CreateHandle, file_wav_end, eSetValueWithOverwrite);
					if(filename[0] != '\0')
					{
						fifo_s_puts(&file_name_fifo, (char *)filename, sizeof(filename));
						memset(filename, 0, sizeof(filename));
						xTaskNotify(Task_Wav_CreateHandle, file_wav_end, eSetValueWithOverwrite);
					}
				}
			}
		}
//		osDelay(10);
	}
	/* USER CODE END AppTask_SD_Write */
}

void AppTask_Wav_Create(void *argument)
{
	uint32_t notifyValue = 0;
	uint8_t file_name[30] = "Chevy.wav";
	uint8_t file_name_ok[35] = {0};
	fifo_s_init(&file_name_fifo, file_name, sizeof(file_name));
	for (;;)
	{
		uint32_t EnterClear = 0x00000000;
		uint32_t ExitClear = 0xFFFFFFFF;
		if(xTaskNotifyWait(EnterClear, ExitClear, &notifyValue, portMAX_DELAY) == pdPASS)
		{
			if(notifyValue == file_wav_end)
			{
				uint16_t fifo_len = 0;
				fifo_len = fifo_s_used(&file_name_fifo);
				fifo_s_gets(&file_name_fifo, (char *)file_name, fifo_len);
				if(strstr((char *)file_name, ".wav") != NULL)
				{
					uint32_t filesize = 0;
					fatTest_GetFileInfo((char *)file_name, &filesize);
					sprintf((char *)file_name_ok, "ok_%s", (char *)file_name);
					while(1)
					{
						static uint32_t file_pos = 0;
						static uint8_t read_buf[3000] = {0};
						static uint16_t read_size = 14;
						static const char str_handle[] = "+IPD,0,";
						FRESULT res;
						res = fatTest_ReadBinFile((TCHAR*)file_name, read_buf, read_size, file_pos);
						if((res == FR_OK) && (read_size == 14))
						{
							char *str_dataSize;
							file_pos += read_size;				//移动文件读取起始位置指针
							str_dataSize = strstr((char *)read_buf, str_handle);
							str_dataSize += sizeof(str_handle) - 1;
							if(str_dataSize[0] != '\0')			//字符串转十进制数值
							{	read_size = atoi(str_dataSize);	}
						}
						else if((res == FR_OK) && (read_size != 14))
						{
							file_pos += read_size;				//移动文件读取起始位置指针
//							fatTest_WriteBinFile((TCHAR*)file_name_ok, read_buf, read_size);

							FRESULT res = f_open(&file, (TCHAR*)file_name_ok, FA_OPEN_APPEND | FA_WRITE);
							if (res == FR_OK)
							{	process_usart_data(read_buf, read_size);}
							f_close(&file);

							memset(read_buf, 0, sizeof(read_buf));
							read_size = 14;
							if((file_pos + read_size) > filesize)
							{
								file_pos = 0;
								memset(read_buf, 0, sizeof(read_buf));
								read_size = 14;
								LED1_Toggle();
								break;
							}
						}
					}
				}
			}
		}
	}
}

#endif

void AppTask_W25Q256(void *argument)
{
	/* USER CODE BEGIN AppTask_W25Q256 */
	norflash_init();
	while (norflash_read_id() != g_norflash_type)
	{
		HAL_UART_Transmit_DMA(&huart1, (uint8_t*) "NG_Norflash!\r\n", 12);
		LED1_Toggle();
		osDelay(250);
	}
	/* Infinite loop */
	for (;;)
	{
		osDelay(10);
	}
	/* USER CODE END AppTask_W25Q256 */
}
